package hasthi.tools;

import hasthi.common.BaseContext;
import hasthi.common.ComponetFactory;
import hasthi.common.ErrorManager;
import hasthi.common.HasthiException;
import hasthi.common.LogFile;
import hasthi.common.SafeScheduler;
import hasthi.common.Utils;
import hasthi.common.XmlBeanUtils;
import hasthi.common.constants.HasthiConstants;
import hasthi.container.CommunicationContianier;
import hasthi.container.MessageContext;
import hasthi.container.MessageProcessor;
import hasthi.container.SimpleMessageContext;
import hasthi.container.UnicastContianier;
import hasthi.manager.AssignResourcesDocument;
import hasthi.manager.ExecuteCommandDocument;
import hasthi.manager.ExecuteCommandResponseDocument;
import hasthi.manager.ManageMeRequestDocument;
import hasthi.manager.ExecuteCommandResponseDocument.ExecuteCommandResponse;
import hasthi.manager.ManageMeRequestDocument.ManageMeRequest;
import hasthi.manager.ManageMeRequestDocument.ManageMeRequest.ResourceDetails;
import hasthi.tools.analyze.TestManager;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.LogManager;
import org.apache.xmlbeans.XmlObject;

public class ManagerSimulatorSet implements MessageProcessor{
    private static Log log = LogFactory.getLog(HasthiConstants.LOGGER_NAME);
    private UnicastContianier contianier;
    private BaseContext baseContext;
    private Map<String, ManagerSimulater> mnagerMap = new ConcurrentHashMap<String, ManagerSimulater>();
    private int repeatCount;
    private String bootstrapsoapAddress;
    private SafeScheduler scheduler;
    
    public ManagerSimulatorSet() throws HasthiException{
        baseContext = new BaseContext();
        contianier = ComponetFactory.createSoapContianier(baseContext, this);
        contianier.startContainer();
        bootstrapsoapAddress = "http://"+baseContext.getBoostrapHost()+":"+baseContext.getBoostrapSoapPort();
        scheduler = new SafeScheduler(50,new SafeScheduler.JobCancelledCallback() {
            public void jobCancelled(Runnable runnable) {
                log.error(runnable + " failed");
            }
        },false);
    }
    
    
    private ManageMeRequestDocument createManageMeRequest(QName resourceName){
        ManageMeRequestDocument manageMeRequestTemplate = ManageMeRequestDocument.Factory.newInstance();
        ManageMeRequest manageMeRequest = manageMeRequestTemplate.addNewManageMeRequest();
        ResourceDetails resourceDetail = manageMeRequest.addNewResourceDetails();
        resourceDetail.setManagmentAgentEpr(contianier.getContainierAddress());
        resourceDetail.setResourceName(resourceName);
        return manageMeRequestTemplate;
    }
    
    
    public void start(int managerCount,int resourcePerManager,float failProb) throws Exception{
        for(int i =0;i<managerCount;i++){
            ManagerSimulater simulater = new ManagerSimulater(baseContext,contianier,"Manager"+i,failProb);
            mnagerMap.put(simulater.getName(), simulater);
            log.info("Manager Simulator "+ contianier.getContainierAddress()+"/"+simulater.getName());
            Thread.sleep(1000);
        }
        
        
        ManageMeRequestDocument manageMeRequestDocement = createManageMeRequest(new QName(Utils.getLocalHost()));
        
        contianier.send(new SimpleMessageContext(manageMeRequestDocement,
                                    bootstrapsoapAddress,HasthiConstants.MANAGE_ME_ACTION));
        
        int serviceCount = managerCount*resourcePerManager;
        
        
        int stepServiceCount = 200;
        repeatCount = (int)Math.ceil((double)serviceCount/stepServiceCount);
        
        for(int j =0;j<repeatCount;j++){
            int createdCount = 0;
            for(String name:TestingUtils.PERSISTANT_SERVIES){
                if(createdCount < stepServiceCount){
                    String serviceName = name +"_"+Utils.getLocalHost()+j+"_"+createdCount;
                    ManageMeRequestDocument manageMeRequestTemplate = createManageMeRequest(new QName("http://extreme.indiana.edu/dummyservice/",serviceName));
                    contianier.send(new SimpleMessageContext(manageMeRequestTemplate,
                            bootstrapsoapAddress,HasthiConstants.MANAGE_ME_ACTION));
                    if(createdCount%10 == 9){
                        Thread.sleep(100);    
                    }
                    createdCount++;
                }
                
            }
            while(createdCount < stepServiceCount){
                int i = Utils.randomInt(TestingUtils.APP_SERVICES.length);
                String serviceName = TestingUtils.APP_SERVICES[i] +"_"+Utils.getLocalHost()+j+"_"+createdCount;
                ManageMeRequestDocument manageMeRequestTemplate = createManageMeRequest(
                        new QName("http://extreme.indiana.edu/dummyservice/",serviceName));
                contianier.send(new SimpleMessageContext(manageMeRequestTemplate,
                        bootstrapsoapAddress,HasthiConstants.MANAGE_ME_ACTION));
                if(createdCount%10 == 9){
                    Thread.sleep(100);    
                }
                createdCount++;
            }
        }
    }

    public void addContainer(CommunicationContianier container) throws HasthiException {
    }

    public XmlObject processMessage(MessageContext messageContext) throws HasthiException {
        String path = messageContext.getSoapEndpoint();
        ManagerSimulater simulater = null;
        if(path != null){
            path = path.replace("/", ""); 
            simulater = mnagerMap.get(path);
        }
        XmlObject request = messageContext.getRequest();
        if(request instanceof AssignResourcesDocument){
            scheduler.submit(new AsyncManagerProcessor(messageContext,simulater));
        }else if(request instanceof ExecuteCommandDocument){
            try {
                ExecuteCommandDocument executeCommandDocument = (ExecuteCommandDocument)request;
                Thread.sleep(100);
                String cmd = executeCommandDocument.getExecuteCommand().getCmd();
                log.info("Simulate create Service "+cmd);
                //int randomGroup = (int)Math.abs(System.currentTimeMillis()%repeatCount);
                String[] commnadParts = cmd.split(" "); 
                String group = commnadParts[3];
                String serviceName = commnadParts[2] +"_"+ group
                    +"_"+System.currentTimeMillis();
                ManageMeRequestDocument manageMeRequestTemplate = createManageMeRequest(
                        new QName("http://extreme.indiana.edu/dummyservice/",serviceName));
                contianier.send(new SimpleMessageContext(manageMeRequestTemplate,
                                            bootstrapsoapAddress,HasthiConstants.MANAGE_ME_ACTION));
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            ExecuteCommandResponseDocument document = ExecuteCommandResponseDocument.Factory.newInstance();
            ExecuteCommandResponse executeCommandResponse = document.addNewExecuteCommandResponse();
            executeCommandResponse.setSucess(true);
            executeCommandResponse.setStdouterr("");
            return document;
        }else{
            String message = XmlBeanUtils.prettyPrint(request);
            if(message.contains("wsdm:removeService")){
                return request;
            }else{
                log.error("Unexpected Message recevied by manager simulator"+ request);
            }
        }
        return null;
    }
    
    
    public void dumpStat(){
        long failureCount = 0;
        for(ManagerSimulater simulater:mnagerMap.values()){
            failureCount = failureCount + simulater.getTestingUtils().getFailureCount();
        }
        try {
            log.info("Simulator at "+ Utils.getLocalHost() +  " "+ failureCount + " resources failed");
            FileWriter w = new FileWriter("start.log",true);
            w.write("Simulator at "+ Utils.getLocalHost() +  " "+ failureCount + " resources failed");
            w.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
    
    public class AsyncManagerProcessor implements Runnable{
        private MessageContext messageContext;
        private ManagerSimulater simulater;
        public AsyncManagerProcessor(MessageContext messageContext, ManagerSimulater simulater) {
            this.simulater = simulater; 
            this.messageContext = messageContext;
            
        }
        public void run(){
            XmlObject request= null;
            try {
                request = messageContext.getRequest();
                if(request instanceof AssignResourcesDocument){
                    simulater.assignResource((AssignResourcesDocument)request);
                }else{
                    log.error("Unknown message at async processing of manager"+XmlBeanUtils.prettyPrint(request));
                }
            } catch (Throwable e) {
                log.warn("Failed to process  "+request 
                        + " "+ e.getMessage(),e);
                ErrorManager.error(e);
            }
        }
    }

    public static void main(String[] args) throws Exception {
//        XmlOptions options = new XmlOptions();
//        options.set
        int managerCount = Integer.parseInt(args[0]);
        int resources4manager = Integer.parseInt(args[1]);
        float failProb;
        if(args.length > 2){
            failProb = Float.parseFloat(args[2]);
            log.info("Using fail rate "+ failProb);
        }else{
            failProb = 0.01f;
        }
        final ManagerSimulatorSet managerSimulatorSet = new ManagerSimulatorSet();
        Thread t = new Thread(new Runnable() {
            public void run() {
                try {
                    //this will die five seconds before the managers
                    Thread.sleep(TestManager.TEST_ROUND_TIME -60*1000);
                    try {
                        managerSimulatorSet.dumpStat();
                        LogFile.flushAll();
                        log.info("Finishing the test! shutting down Log");
                        LogManager.shutdown();
                    } catch (RuntimeException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.exit(0);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        
        });
        t.setDaemon(true);
        t.start();
        
        managerSimulatorSet.start(managerCount, resources4manager,failProb);
    }
}
